import { Element } from './element';
import $ from 'jquery'
import types from './types';

let diffQueue = []; // 差异队列
let updateDepth = 0; // 更新的级别

class Unit{
  constructor(element) {
    this._currentElement = element
  }
  getMarkUp() {
    throw Error('此方法不能被调用')
  }
}

class TextUnit extends Unit{
  update(nextElement) {
    if(this._currentElement !== nextElement){
      this._currentElement = nextElement;
      $(`[data-reactid="${this._reactid}"]`).html(this._currentElement)
    }
    
  }
  getMarkUp(reactid) {
    this._reactid = reactid
    return `<span data-reactid="${reactid}">${this._currentElement}</span>`
  }
}

class NativeUnit extends Unit {
  getMarkUp(reactid) {
    this._reactid = reactid
    let {type, props} = this._currentElement
    let tagStart = `<${type} data-reactid="${this._reactid}" `
    let childString = '';
    let tagEnd = `</${type}>`
    this._renderedChildrenUnits = [];
    for(let propName in props) {
      if(!props.hasOwnProperty(propName)) return 
      if(/^on[A-Z]/.test(propName)) {
        let eventName = propName.slice(2).toLowerCase()
        $(document).delegate(`[data-reactid="${this._reactid}"]`, `${eventName}.${this._reactid}`, props[propName])
      }else if(propName === 'style') {
        let styleObj = props[propName]
        let styles = Object.entries(styleObj).map(([attr, value]) => {
          attr = attr.replace(/([A-Z])/g, function(matched, $1){
            return `-${$1.toLowerCase()}`}
          )
          return `${attr}:${value}`
        }).join(';')
        tagStart += (` style="${styles}"`)
      }else if(propName === 'className'){
        tagStart += (` class="${props[propName]}" `)
      }else if(propName === 'children') {
        let children = props[propName]
        // eslint-disable-next-line no-loop-func
        children.forEach((child, index) => {
          let childUnit = createUnit(child)
          childUnit._mountIndex = index; // 下划线index 挂载索引
          this._renderedChildrenUnits.push(childUnit);
          let childMarkup = childUnit.getMarkUp(`${this._reactid}.${index}`)
          childString += childMarkup
        })
      }else{
        tagStart += (` ${propName}="${props[propName]}" `)
      }
    }
    return tagStart + '>' + childString +tagEnd
  }
  update(nextElement) {
    let oldProps = this._currentElement.props
    let newProps = nextElement.props
    this.updateDOMProperties(oldProps, newProps)
    this.updateDOMChildren(nextElement.props.children)
  }
  // 新的和老的对比 找出差异
  updateDOMChildren(newChildrenElements) {
    // updateDepth++;
    this.diff(diffQueue, newChildrenElements)
    // updateDepth--;
    console.log(diffQueue)
    // if(updateDepth === 0) {
    //   this.patch(diffQueue)
    //   diffQueue = []
    // }
    // console.log(diffQueue)
  }
  patch(diffQueue) {
    let deleteChildren = []
    let deleteMap = {}
    for(let i = 0;i<diffQueue.length; i++) {
      let difference = diffQueue[i]
      if(difference.type === types.MOVE || difference.type === types.REMOVE){
        let fromIndex = difference.fromIndex
        let oldChild = $(difference.parentNode.children().get(fromIndex))
        deleteMap[fromIndex] = oldChild
        deleteChildren.push(oldChild)
      }
    }
    $.each(deleteChildren, (idx, item)=>$(item).remove());

    // for(let i=0; i<diffQueue.length; i++) {
    //   let difference = diffQueue[i]
    //   switch(difference.type) {
    //     case types.INSERT:
    //       this.insertChildAt(difference.parentNode, difference.toIndex, $(difference.markUp));
    //       break;
    //     case types.MOVE:
    //       this.insertChildAt(difference.parentNode, difference.toIndex, deleteMap[difference.fromIndex]);
    //       break;
    //     default:
    //       break;
    //   }
    // }
  }
  insertChildAt(parentNode, index, newNode) {
    let oldChild = parentNode.children().get(index)
    oldChild ? newNode.insertBefore(oldChild) : newNode.appendTo(parentNode)
  }
  diff(diffQueue, newChildrenElements) {
    let oldChildrenUnitMap = this.getOldChildrenMap(this._renderedChildrenUnits);
    // console.log(oldChildrenUnitMap, 'oldChildrenUnitMap')
    // 新的units数组
    let {newChildrenUnits, newChildrenUnitMap} = this.getNewChildren(oldChildrenUnitMap, newChildrenElements)
    let lastIndex = 0
    for(let i=0; i<newChildrenUnits.length; i++) {
      let newUnit = newChildrenUnits[i]
      let newKey = (newUnit._currentElement.props&&newUnit._currentElement.props.key) || i.toString
      let oldChildUnit = oldChildrenUnitMap[newKey]
      if(oldChildUnit === newUnit) { // 一致的话 服用老街店
        if(oldChildUnit._mountIndex < lastIndex) {
          diffQueue.push({
            parentId: this._reactid,
            parentNode: $(`[data-reactid="${this._reactid}"]`),
            type: types.MOVE,
            fromIndex: oldChildUnit._mountIndex,
            toIndex: i
          })
          lastIndex = Math.max(lastIndex, oldChildUnit._mountIndex)
        }
        newUnit._mountIndex = i
      }else{
        diffQueue.push({
          parentId: this._reactid,
          parentNode: $(`[data-reactid="${this._reactid}"]`),
          type: types.INSERT,
          toIndex: i,
          markUp: newUnit.getMarkUp(`${this._reactid}.${i}`)
        })
      }
    }
    for(let oldKey in oldChildrenUnitMap){
      let oldChild = oldChildrenUnitMap[oldKey]
      if(!newChildrenUnitMap[oldKey]) {
        diffQueue.push({
          parentId: this._reactid,
          parentNode: $(`[data-reactid="${this._reactid}"]`),
          type: types.REMOVE,
          fromIndex: oldChild._mountIndex
        })
      }
    }
  }
  getNewChildren(oldChildrenUnitMap, newChildrenElements) {
    let newChildrenUnits = []
    let newChildrenUnitMap = {};
    newChildrenElements.forEach((newElement, index)=>{
      let newKey = (newElement.props&&newElement.props.key) || index.toString();
      let oldUnit = oldChildrenUnitMap[newKey];
      let oldElement = oldUnit && oldUnit._currentElement;
      // console.log(newElement)
      
      if(shouldDeepCompare(oldElement, newElement)) {
        // console.log(oldUnit)
        oldUnit.update(newElement)
        newChildrenUnits.push(oldUnit)
        newChildrenUnitMap[newKey] = oldUnit
      }else{
        let nextUnit = createUnit(newElement);
        newChildrenUnits.push(nextUnit)
        newChildrenUnitMap[newKey] = nextUnit
      }
    })
    return {
      newChildrenUnitMap,
      newChildrenUnits
    }
  }
  getOldChildrenMap(childrenUnits = []) {
    let map = {}
    for(let i = 0; i<childrenUnits.length;i++){
      let unit = childrenUnits[i]
      let key = (unit && unit._currentElement && unit._currentElement.props && unit._currentElement.props.key) || i.toString();
      map[key] = unit;
    }
    return map;
  }
  updateDOMProperties(oldProps, newProps) {
    // 
    let propName
    for(propName in oldProps) {
      if(!newProps.hasOwnProperty(propName)){
        $(`[data-reactid="${this._reactid}"]`).removeAttr(propName)
      }
      if(/^on[A-Z]/.test(propName)) {
        $(document).undelegate(`.${this._reactid}`)
      }
    }
    for(propName in newProps) {
      if(propName === 'children'){
        continue
      }else if(/^on[A-Z]/.test(propName)) {
        let eventName = propName.slice(2).toLowerCase()
        $(document).delegate(`[data-reactid="${this._reactid}"]`, `${eventName}.${this._reactid}`, newProps[propName])
      }else if(propName === 'style') {
        let styleObj = newProps[propName]
        Object.entries(styleObj).forEach(([attr, value]) => {
          $(`[data-reactid="${this._reactid}"]`).css(attr, value)
        })
      }else if(propName === 'className'){
        $(`[data-reactid="${this._reactid}"]`)[0].className = newProps[propName]
      }else{
        $(`[data-reactid="${this._reactid}"]`).prop(propName, newProps[propName])
      }

    }
  }
}

class CompositeUnit extends Unit{
  // 负责处理组件的更新操作
  update(nextElement, partialState) {
    this._currentElement = nextElement || this._currentElement
    // 新的属性对象
    // 不管你要不要更新 我先把状态改了
    let nextState = this._componentInstance.state = Object.assign(this._componentInstance.state, partialState)
    let nextProps = this._currentElement.props;
    if(this._componentInstance.shouldComponentUpdate && !this._componentInstance.shouldComponentUpdate(nextProps, nextState)) {
      return
    }
    // 下面要进行比较更新了
    let preRenderedUnitInstance = this._renderedUnitInstance
    let preRenderedElement = preRenderedUnitInstance._currentElement
    let nextRenderElement = this._componentInstance.render()
    // 新旧两个元素类型一样 则可以进行深度比较
    if(shouldDeepCompare(preRenderedElement, nextRenderElement)) {
      preRenderedUnitInstance.update(nextRenderElement)
      this._componentInstance.componentDidUpdate && this._componentInstance.componentDidUpdate()
    }else{
      this._renderedUnitInstance = createUnit(nextRenderElement)
      let nextRenderMarkUp = this._renderedUnitInstance.getMarkUp(this._reactid)
      $(`[data-reactid="${this._reactid}"]`).replaceWith(nextRenderMarkUp)
    }
  }
  getMarkUp(reactid) {
    this._reactid = reactid
    let {type: Component, props} = this._currentElement
    let componentInstance = this._componentInstance = new Component(props)
    componentInstance._currentUnit = this;
    componentInstance.componentWillMount && componentInstance.componentWillMount()
    let renderedElement = componentInstance.render()
    let renderedUnit = this._renderedUnitInstance = createUnit(renderedElement)
    let renderMarkUp = renderedUnit.getMarkUp(this._reactid)
    // 在这个时候绑定一个事件
    $(document).on('mounted',()=>{
      componentInstance.componentDidMount && componentInstance.componentDidMount()
    })
    return renderMarkUp
  }
}

function shouldDeepCompare(oldEle, newEle) {
  if(oldEle !== null &&  newEle !== null) {
    let oldType = typeof oldEle
    let newType = typeof newEle
    if((oldType === 'string' || oldType === 'number') && (newType === 'string' || newType === 'number')){
      return true
    }
    if(oldEle instanceof Element && newEle instanceof Element ){
      return oldEle.type === newEle.type
    }
  }
  return false
}

export function createUnit(element) {
  if(typeof element === 'string' || typeof element === 'number'){
    return new TextUnit(element);
  }
  // 这边是原生的一些元素 比如说 div什么的
  if(element instanceof Element && typeof element.type === 'string') {
    return new NativeUnit(element)
  }
  // 是一个组件
  if(element instanceof Element && typeof element.type === 'function') {
    return new CompositeUnit(element)
  }
}
